package deepbrain.iit;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import deepbrain.simnetwork.exception.SimNetworkRuntimeException;

/**
 * Generate partitions using external application Before generating partitions,
 * set command path first;
 * 
 * @author Li Yang
 * @create 2009-3-10
 */
public class PartitionGeneratorExt implements Iterable<Partition<Integer>> {

	public static String GENERATE_PARTITIONS_COMMAND = "D:/gen_partitions1.exe";

	private int numOfNodes;
	private int maxNumOfParts;

	public PartitionGeneratorExt(int numOfNodes, int maxNumOfParts) {
		this.numOfNodes = numOfNodes;
		this.maxNumOfParts = maxNumOfParts;
	}

	/**
	 * Parse Partitions in lines from InputStream source
	 */
	public static List<Partition<Integer>> parsePartitions(InputStream source,
			int numOfPartitions) {
		return parsePartitions(
				new BufferedReader(new InputStreamReader(source)),
				numOfPartitions);
	}

	/**
	 * Parse Partitions in lines from Reader source
	 */
	public static List<Partition<Integer>> parsePartitions(Reader source,
			int numOfPartitions) {
		BufferedReader reader;
		if (source instanceof BufferedReader)
			reader = (BufferedReader) source;
		else
			reader = new BufferedReader(source);
		List<Partition<Integer>> partitions = new ArrayList<Partition<Integer>>();
		String line = null;
		int parsedNum = 0;
		while (true) {
			try {
				line = reader.readLine();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
			if (line == null)
				break;
			partitions.add(Partition.parsePartition(line));
			if ((++parsedNum) >= numOfPartitions)
				break;
		}
		return partitions;
	}

	public static List<Partition<Integer>> parsePartitions(InputStream source) {
		return parsePartitions(
				new BufferedReader(new InputStreamReader(source)),
				Integer.MAX_VALUE);
	}

	public static List<Partition<Integer>> parsePartitions(Reader source) {
		return parsePartitions(source, Integer.MAX_VALUE);
	}

	public static List<Partition<Integer>> generatePartitions(int numOfNodes,
			int numOfParts, String GENERATE_PARTITIONS_COMMAND) {
		ProcessBuilder builder = new ProcessBuilder(new String[] {
				GENERATE_PARTITIONS_COMMAND, Integer.toString(numOfNodes),
				Integer.toString(numOfParts) });
		Process process = null;
		try {
			process = builder.start();
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
		InputStream source = process.getInputStream();
		List<Partition<Integer>> partitions = parsePartitions(source);
		return partitions;
	}

	public Iterator<Partition<Integer>> iterator() {
		return new Iter();
	}

	private class Iter implements Iterator<Partition<Integer>> {

		private boolean finish = false;
		private String line = null;
		private BufferedReader source;
		private boolean parsed;
		private Partition<Integer> last;

		public Iter() {
			ProcessBuilder builder = new ProcessBuilder(new String[] {
					GENERATE_PARTITIONS_COMMAND, Integer.toString(numOfNodes),
					Integer.toString(maxNumOfParts) });
			Process process = null;
			try {
				process = builder.start();
			} catch (IOException e) {
				e.printStackTrace();
				finish = true;
			}
			source = new BufferedReader(new InputStreamReader(process
					.getInputStream()));
		}

		public boolean hasNext() {
			if (finish)
				return false;
			try {
				line = source.readLine();
			} catch (IOException e) {
				throw new SimNetworkRuntimeException(e);
			}
			if (line == null)
				return false;
			parsed = false;
			return true;
		}

		public Partition<Integer> next() {
			if (!parsed) {
				last = Partition.parsePartition(line);
				parsed = true;
			}
			return last;
		}

		public void remove() {
			throw new UnsupportedOperationException();
		}

	}

	public static void main(String[] args) {
		long startTime = System.currentTimeMillis();
		for (Partition<Integer> partition : new PartitionGeneratorExt(12, 5)) {
			System.out.println(partition);
		}
		System.out.println(System.currentTimeMillis() - startTime);
	}
}
